/* tolua: functions to push C values.
** Support code for Lua bindings.
** Written by Waldemar Celes
** TeCGraf/PUC-Rio
** Apr 2003
** $Id: $
*/

/* This code is free software; you can redistribute it and/or modify it.
** The software provided hereunder is on an "as is" basis, and
** the author has no obligation to provide maintenance, support, updates,
** enhancements, or modifications.
*/

#include "tolua++.h"
#include "lauxlib.h"

#include <stdlib.h>

TOLUA_API void tolua_pushvalue (lua_State* L, int lo)
{
 lua_pushvalue(L,lo);
}

TOLUA_API void tolua_pushboolean (lua_State* L, int value)
{
 lua_pushboolean(L,value);
}

TOLUA_API void tolua_pushnumber (lua_State* L, lua_Number value)
{
 lua_pushnumber(L,value);
}

TOLUA_API void tolua_pushstring (lua_State* L, const char* value)
{
 if (value == NULL)
  lua_pushnil(L);
 else
  lua_pushstring(L,value);
}

TOLUA_API void tolua_pushuserdata (lua_State* L, void* value)
{
 if (value == NULL)
  lua_pushnil(L);
 else
  lua_pushlightuserdata(L,value);
}

TOLUA_API void tolua_pushusertype (lua_State* L, void* value, const char* type)
{
 if (value == NULL)
  lua_pushnil(L);
 else
 {
  luaL_getmetatable(L, type);
  lua_pushstring(L,"tolua_ubox");
  lua_rawget(L,-2);        /* stack: mt ubox */
  if (lua_isnil(L, -1)) {
	  lua_pop(L, 1);
	  lua_pushstring(L, "tolua_ubox");
	  lua_rawget(L, LUA_REGISTRYINDEX);
  };
  lua_pushlightuserdata(L,value);
  lua_rawget(L,-2);                       /* stack: mt ubox ubox[u] */
  if (lua_isnil(L,-1))
  {
   lua_pop(L,1);                          /* stack: mt ubox */
   lua_pushlightuserdata(L,value);
   *(void**)lua_newuserdata(L,sizeof(void *)) = value;   /* stack: mt ubox u newud */
   lua_pushvalue(L,-1);                   /* stack: mt ubox u newud newud */
   lua_insert(L,-4);                      /* stack: mt newud ubox u newud */
   lua_rawset(L,-3);                      /* stack: mt newud ubox */
   lua_pop(L,1);                          /* stack: mt newud */
   /*luaL_getmetatable(L,type);*/
   lua_pushvalue(L, -2);			/* stack: mt newud mt */
   lua_setmetatable(L,-2);			/* stack: mt newud */

   #ifdef LUA_VERSION_NUM
   lua_pushvalue(L, TOLUA_NOPEER);
#if LUA_VERSION_NUM > 501
   lua_setuservalue(L, -2);
#else
   lua_setfenv(L, -2);
#endif
   #endif
  }
  else
  {
   /* check the need of updating the metatable to a more specialized class */
   lua_insert(L,-2);                       /* stack: mt ubox[u] ubox */
   lua_pop(L,1);                           /* stack: mt ubox[u] */
   lua_pushstring(L,"tolua_super");
   lua_rawget(L,LUA_REGISTRYINDEX);        /* stack: mt ubox[u] super */
   lua_getmetatable(L,-2);                 /* stack: mt ubox[u] super mt */
   lua_rawget(L,-2);                       /* stack: mt ubox[u] super super[mt] */
			if (lua_istable(L,-1))
			{
				lua_pushstring(L,type);                 /* stack: mt ubox[u] super super[mt] type */
				lua_rawget(L,-2);                       /* stack: mt ubox[u] super super[mt] flag */
				if (lua_toboolean(L,-1) == 1)   /* if true */
				{
					lua_pop(L,3);	/* mt ubox[u]*/
					lua_remove(L, -2);
					return;
				}
			}
			/* type represents a more specilized type */
			/*luaL_getmetatable(L,type);             // stack: mt ubox[u] super super[mt] flag mt */
			lua_pushvalue(L, -5);					/* stack: mt ubox[u] super super[mt] flag mt */
			lua_setmetatable(L,-5);                /* stack: mt ubox[u] super super[mt] flag */
			lua_pop(L,3);                          /* stack: mt ubox[u] */
  }
  lua_remove(L, -2);	/* stack: ubox[u]*/
 }
}

TOLUA_API void tolua_pushusertype_and_takeownership (lua_State* L, void* value, const char* type)
{
	tolua_pushusertype(L,value,type);
	tolua_register_gc(L,lua_gettop(L));
}

TOLUA_API void tolua_pushfieldvalue (lua_State* L, int lo, int index, int v)
{
 lua_pushnumber(L,index);
 lua_pushvalue(L,v);
 lua_settable(L,lo);
}

TOLUA_API void tolua_pushfieldboolean (lua_State* L, int lo, int index, int v)
{
 lua_pushnumber(L,index);
 lua_pushboolean(L,v);
 lua_settable(L,lo);
}


TOLUA_API void tolua_pushfieldnumber (lua_State* L, int lo, int index, lua_Number v)
{
 lua_pushnumber(L,index);
 tolua_pushnumber(L,v);
 lua_settable(L,lo);
}

TOLUA_API void tolua_pushfieldstring (lua_State* L, int lo, int index, const char* v)
{
 lua_pushnumber(L,index);
 tolua_pushstring(L,v);
 lua_settable(L,lo);
}

TOLUA_API void tolua_pushfielduserdata (lua_State* L, int lo, int index, void* v)
{
 lua_pushnumber(L,index);
 tolua_pushuserdata(L,v);
 lua_settable(L,lo);
}

TOLUA_API void tolua_pushfieldusertype (lua_State* L, int lo, int index, void* v, const char* type)
{
 lua_pushnumber(L,index);
 tolua_pushusertype(L,v,type);
 lua_settable(L,lo);
}

TOLUA_API void tolua_pushfieldusertype_and_takeownership (lua_State* L, int lo, int index, void* v, const char* type)
{
 lua_pushnumber(L,index);
 tolua_pushusertype(L,v,type);
	tolua_register_gc(L,lua_gettop(L));
 lua_settable(L,lo);
}

